/* Copyright (C) 2010 Luca Piccinelli
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <opencv2/imgproc/imgproc.hpp>

#include "imgops/thresholding/CvHysteresisThreshOp.h"
#include "imgops/thresholding/functions/thresholding.h"
#include "factory/factories.h"

using namespace cv;

namespace NAMESPACE{

// ****** Functor implementation ***********************************************
bool CvHysteresisThreshFunc::operator()(const cv::Mat& src,
                                        cv::Mat& dst,
                                        uchar t1,
                                        uchar t2,
                                        uchar v1,
                                        uchar v2,
                                        int block_size){
    Mat m = src;
    if(src.type() == cv::DataType<double>::type){
        double max = *std::max_element(src.begin<double>(), src.end<double>());
        src.convertTo(m, cv::DataType<uchar>::type, 255.0 / max);
    }
    hysteresis_thresh(m, dst, t1, t2, v1, v2, block_size);
    return true;
}
// -----------------------------------------------------------------------------

// ****** Constants assignment *************************************************
const size_t CvHysteresisThreshOp::MIN_INPUT_NUM = 1;
const size_t CvHysteresisThreshOp::MIN_CRITERIA_NUM = 4;
const size_t CvHysteresisThreshOp::MIN_OUTPUT_NUM = 1;
// -----------------------------------------------------------------------------

// ****** Operation implementation *********************************************
CvHysteresisThreshOp::~CvHysteresisThreshOp(){}
CvHysteresisThreshOp::CvHysteresisThreshOp() : AbstractOperation<CvHysteresisThreshFunc>(){

    min_input_num = MIN_INPUT_NUM;
    min_criteria_num = MIN_CRITERIA_NUM;
    min_output_num = MIN_OUTPUT_NUM;

    k1.set_key(INPUT_NS,  "img"); k1.setType(TypeParseTraits<cv::Mat*>::name());
    k2.set_key(OUTPUT_NS, "img"); k2.setType(TypeParseTraits<cv::Mat*>::name());

    k3.set_key(CRITERIA_NS, "threshold.1");   k3.setType(TypeParseTraits<double>::name());
    PDLRange k3_range(0, 255, 1);             k3.setAcceptedValues(k3_range);
    k4.set_key(CRITERIA_NS, "threshold.2");   k4.setType(TypeParseTraits<double>::name());
    PDLRange k4_range(0, 255, 1);             k4.setAcceptedValues(k4_range);
    k5.set_key(CRITERIA_NS, "value.1");       k5.setType(TypeParseTraits<int>::name());
    PDLRange k5_range(0, 255, 1);             k5.setAcceptedValues(k5_range);
    k6.set_key(CRITERIA_NS, "value.2");       k6.setType(TypeParseTraits<int>::name());
    PDLRange k6_range(0, 255, 1);             k6.setAcceptedValues(k6_range);
    k7.set_key(CRITERIA_NS, "size");          k7.setType(TypeParseTraits<int>::name());
    PDLRange k7_range(1, 7, 2);               k7.setAcceptedValues(k7_range);

    input_keys->push_back(k1);
    output_keys->push_back(k2);

    criteria_keys->push_back(k3);
    criteria_keys->push_back(k4);
    criteria_keys->push_back(k5);
    criteria_keys->push_back(k6);
    criteria_keys->push_back(k7);
}
CvHysteresisThreshOp::CvHysteresisThreshOp(const CvHysteresisThreshOp& gbo) : AbstractOperation<CvHysteresisThreshFunc>(gbo){}
CvHysteresisThreshOp& CvHysteresisThreshOp::operator=(const CvHysteresisThreshOp& gbo){
    if(this == &gbo) return *this;
    AbstractOperation<CvHysteresisThreshFunc>::operator=(gbo);
    return *this;
}

bool CvHysteresisThreshOp::doOperation(const std::list<input_t>& inputs,
                              const std::list<criteria_t>& operationCriteria,
                              std::list<output_t>& outputs) throw(bad_io_mapping) {

    AbstractOperation<CvHysteresisThreshFunc>::prepareIO(inputs, operationCriteria, outputs);

    cv::Mat* in_img  = boost::any_cast<cv::Mat*>( ((*input_mapping) [k1])->getElement() );
    cv::Mat* out_img = boost::any_cast<cv::Mat*>( ((*output_mapping)[k2])->getElement() );

    int t1 = boost::any_cast<double>( ((*criteria_mapping)[k3])->getElement() );
    int t2 = boost::any_cast<double>( ((*criteria_mapping)[k4])->getElement() );
    int v1 = boost::any_cast<int>( ((*criteria_mapping)[k5])->getElement() );
    int v2 = boost::any_cast<int>( ((*criteria_mapping)[k6])->getElement() );

    // Optional criteria
    IOElement*  block_size_io = (*criteria_mapping)[k7];
    int block_size = block_size_io ? boost::any_cast<int>( block_size_io->getElement() ) : 3;

    (*operation)(*in_img, *out_img, t1, t2, v1, v2, block_size);

    return true;
}

bool CvHysteresisThreshOp::operator==(const AbstractOperation<operation_t>& op) const{
    return AbstractOperation<CvHysteresisThreshFunc>::operator==(op);
}

bool CvHysteresisThreshOp::operator!=(const AbstractOperation<operation_t>& op) const{
    return !operator==(op);
}
// -----------------------------------------------------------------------------

REGISTER_PARSE_TYPE(CvHysteresisThreshOp);
SimpleFactoryImpl<PipeStepComputation, CvHysteresisThreshOp> cvHysteresisThreshOpFactory_instance;
}
